package com.yixing.tech.common.utils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * DES加密解密工具类
 * 使用固定密钥进行DES加密和解密操作
 */
public class DESUtil {

    /**
     * DES加密固定密钥
     */
    public final static String passwordKey = "uftfdb8e";

    /**
     * DES加密方法，使用固定密钥
     *
     * @param datasource 待加密的字节数组
     * @return 加密后的字节数组
     */
    public static byte[] encrypt(byte[] datasource) {
        try {
            byte[] keyBytes = passwordKey.getBytes("UTF-8");
            if(keyBytes.length != 8) {
                // 确保密钥长度是8字节
                if(keyBytes.length > 8) {
                    keyBytes = java.util.Arrays.copyOf(keyBytes, 8);
                } else {
                    // 如果长度不足8，用0填充
                    byte[] paddedKey = new byte[8];
                    System.arraycopy(keyBytes, 0, paddedKey, 0, keyBytes.length);
                    keyBytes = paddedKey;
                }
            }
            DESKeySpec desKey = new DESKeySpec(keyBytes);
            // 创建一个密匙工厂，然后用它把DESKeySpec转换成
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(desKey);
            // Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(keyBytes);
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, securekey, iv);
            // 现在，获取数据并加密
            // 正式执行加密操作
            return cipher.doFinal(datasource);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * DES解密方法，使用固定密钥 - PKCS5填充模式
     *
     * @param src 待解密的字节数组
     * @return 解密后的字节数组
     * @throws Exception 解密异常
     */
    public static byte[] decryptPk(byte[] src) throws Exception {
        byte[] keyBytes = passwordKey.getBytes("UTF-8");
        if(keyBytes.length != 8) {
            // 确保密钥长度是8字节
            if(keyBytes.length > 8) {
                keyBytes = java.util.Arrays.copyOf(keyBytes, 8);
            } else {
                // 如果长度不足8，用0填充
                byte[] paddedKey = new byte[8];
                System.arraycopy(keyBytes, 0, paddedKey, 0, keyBytes.length);
                keyBytes = paddedKey;
            }
        }
        // DES算法要求有一个可信任的随机数源
        SecureRandom random = new SecureRandom();
        // 创建一个DESKeySpec对象
        DESKeySpec desKey = new DESKeySpec(keyBytes);
        // 创建一个密匙工厂
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        // 将DESKeySpec对象转换成SecretKey对象
        SecretKey securekey = keyFactory.generateSecret(desKey);
        // Cipher对象实际完成解密操作
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(keyBytes);
        // 用密匙初始化Cipher对象
        cipher.init(Cipher.DECRYPT_MODE, securekey, iv, random);
        // 真正开始解密操作
        return cipher.doFinal(src);
    }

    /**
     * DES解密方法，使用固定密钥 - 无填充模式
     *
     * @param src 待解密的字节数组
     * @return 解密后的字节数组
     * @throws Exception 解密异常
     */
    public static byte[] decryptNo(byte[] src) throws Exception {
        byte[] keyBytes = passwordKey.getBytes("UTF-8");
        if(keyBytes.length != 8) {
            // 确保密钥长度是8字节
            if(keyBytes.length > 8) {
                keyBytes = java.util.Arrays.copyOf(keyBytes, 8);
            } else {
                // 如果长度不足8，用0填充
                byte[] paddedKey = new byte[8];
                System.arraycopy(keyBytes, 0, paddedKey, 0, keyBytes.length);
                keyBytes = paddedKey;
            }
        }
        // DES算法要求有一个可信任的随机数源
        SecureRandom random = new SecureRandom();
        // 创建一个DESKeySpec对象
        DESKeySpec desKey = new DESKeySpec(keyBytes);
        // 创建一个密匙工厂
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        // 将DESKeySpec对象转换成SecretKey对象
        SecretKey securekey = keyFactory.generateSecret(desKey);
        // Cipher对象实际完成解密操作
        //Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        Cipher cipher = Cipher.getInstance("DES/CBC/NoPadding");
        IvParameterSpec iv = new IvParameterSpec(keyBytes);
        // 用密匙初始化Cipher对象
        cipher.init(Cipher.DECRYPT_MODE, securekey, iv, random);
        // 真正开始解密操作
        return cipher.doFinal(src);
    }

    /**
     * DES解密方法，使用固定密钥 - 自动尝试PKCS5填充和无填充模式
     *
     * @param src 待解密的字节数组
     * @return 解密后的字节数组
     * @throws Exception 解密异常
     */
    public static byte[] decrypt(byte[] src) throws Exception {
        try {
            return decryptPk(src);
        } catch (BadPaddingException e) {
            return decryptNo(src);
        }
    }


    /**
     * 将字节数组转换为十六进制字符串
     *
     * @param bArray 字节数组
     * @return 十六进制字符串
     */
    public static final String bytesToHexString(byte[] bArray) {
        StringBuffer sb = new StringBuffer(bArray.length);
        String sTemp;
        for (int i = 0; i < bArray.length; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            if (sTemp.length() < 2)
                sb.append(0);
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 将十六进制字符串转换为字节数组
     *
     * @param hex 十六进制字符串
     * @return 字节数组
     */
    public static byte[] hexStringToByte(String hex) {
        int len = (hex.length() / 2); //除以2是因为十六进制比如a1使用两个字符代表一个byte
        byte[] result = new byte[len];
        char[] achar = hex.toCharArray();
        for (int i = 0; i < len; i++) {
            //乘以2是因为十六进制比如a1使用两个字符代表一个byte,pos代表的是数组的位置
            //第一个16进制数的起始位置是0第二个是2以此类推
            int pos = i * 2;

            //<<4位就是乘以16  比如说十六进制的\"11\",在这里也就是1*16|1,而其中的\"|\"或运算就相当于十进制中的加法运算
            //如00010000|00000001结果就是00010001 而00010000就有点类似于十进制中10而00000001相当于十进制中的1，与是其中的或运算就相当于是10+1(此处说法可能不太对，)
            result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1]));
        }
        return result;
    }

    /**
     * 解密十六进制字符串格式的密文
     *
     * @param content 待解密的十六进制字符串
     * @return 解密后的字符串
     */
    public static String decrypt(String content) {
        try {
            byte[] b = DESUtil.decrypt(DESUtil.hexStringToByte(content));
            return new String(b, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 加密字符串为十六进制格式的密文
     *
     * @param content 待加密的字符串
     * @return 加密后的十六进制字符串
     */
    public static String encrypt(String content) {
        try {
            byte[] result = DESUtil.encrypt(content.getBytes("UTF-8"));
            return DESUtil.bytesToHexString(result);//加密后的密文
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * AES-CBC解密
     *
     * @param contentBase64 待解密的数据(Base64编码)
     * @param keyBase64     密钥(Base64编码)
     * @param ivBase64      初始化向量(Base64编码)
     * @return 解密后的字符串
     * @throws Exception 解密异常
     */
    public static String decryptAES(String contentBase64, String keyBase64, String ivBase64) throws Exception {
        Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
        //key
        byte[] skAsByteArray = Base64.getDecoder().decode(keyBase64);
        SecretKeySpec aesKey = new SecretKeySpec(skAsByteArray, "AES");
        //iv
        IvParameterSpec aesIV = new IvParameterSpec(Base64.getDecoder().decode(ivBase64));
        c.init(Cipher.DECRYPT_MODE, aesKey, aesIV);
        return new String(c.doFinal(Base64.getDecoder().decode(contentBase64)));

    }


    /**
     * 将字符转换为字节
     *
     * @param c 十六进制字符
     * @return 字节值
     */
    private static byte toByte(char c) {
        return  (byte) "0123456789ABCDEF".indexOf(c);
    }

}
